/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package questionbook.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.openrdf.model.Literal;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.vocabulary.RDF;
import questionbook.Configuration.QuestionbookConfiguration;
import questionbook.exception.IndividualAlreadyExistsException;
import questionbook.manager.StoreManager;
import questionbook.model.Answer;
import questionbook.model.Category;
import questionbook.model.Person;
import questionbook.model.UserClass;
import questionbook.service.common.GenericServiceImpl;
import questionbook.standard.Foaf;
import questionbook.standard.Qbook;

/**
 *
 * @author stefanstift
 */
public class PersonServiceBean extends GenericServiceImpl implements PersonService
{

    public PersonServiceBean() {

        super();
    }

    public void persist(Person p) {

        this.storeManager.getStore().add(p.getUri(), RDF.TYPE,
                                         Foaf.getInstance().getPersonURI());
        this.storeManager.getStore().add(p.getUri(), 
                                         Qbook.getInstance().getCreatedAtURI(),
                                         p.getCreatedAtLiteral());
        this.storeManager.getStore().add(p.getUri(), 
                                         Foaf.getInstance().getMboxURI(),
                                         p.getMboxLiteral());
        this.storeManager.getStore().add(p.getUri(), 
                                         Foaf.getInstance().getFirstNameURI(),
                                         p.getFirstNameLiteral());
        this.storeManager.getStore().add(p.getUri(), 
                                         Foaf.getInstance().getLastNameURI(),
                                         p.getLastNameLiteral());
        this.storeManager.getStore().add(p.getUri(), Qbook.getInstance().
                                         getMd5PasswordURI(),
                                         p.getMd5PasswordLiteral());

        if(p.getExtLongId() > 0)
             this.storeManager.getStore().add(p.getUri(), 
                            Qbook.getInstance().getExtLongIdURI(),
                            p.getExtLongIdLiteral());

        if(p.getExtStringId() != null && !p.getExtStringId().isEmpty())
            this.storeManager.getStore().add(p.getUri(), 
                            Qbook.getInstance().getExtStringIdURI(),
                            p.getExtStringIdLiteral());

    }

    public Person get(URI uri)
    {
        Person p = new Person(uri);

        List<Statement> fn = this.storeManager.getStore().tuplePattern(uri, Foaf.getInstance().getFirstNameURI(), null);
        List<Statement> ln = this.storeManager.getStore().tuplePattern(uri, Foaf.getInstance().getLastNameURI(), null);
        List<Statement> mb = this.storeManager.getStore().tuplePattern(uri, Foaf.getInstance().getMboxURI(), null);
        List<Statement> ca = this.storeManager.getStore().tuplePattern(uri, Qbook.getInstance().getCreatedAtURI(), null);
        List<Statement> extLong = this.storeManager.getStore().tuplePattern(uri, Qbook.getInstance().getExtLongIdURI(), null);
        List<Statement> extString = this.storeManager.getStore().tuplePattern(uri, Qbook.getInstance().getExtStringIdURI(), null);
       
        if(fn != null && fn.size() > 0)
            p.setFirstName(fn.get(0).getObject().stringValue());
        if(ln != null && ln.size() > 0)
            p.setLastName(ln.get(0).getObject().stringValue());
        if(mb != null && mb.size() > 0)
            p.setMbox(mb.get(0).getObject().stringValue());
        if(ca != null && ca.size() > 0)
            p.setCreatedAt(ca.get(0).getObject().stringValue());
        if(extLong != null && extLong.size() > 0)
            p.setExtLongId(extLong.get(0).getObject().stringValue());
        if(extString != null && extString.size() > 0)
            p.setExtStringId(extString.get(0).getObject().stringValue());

        this.loadUserClass(p);

        return p;
    }

    public void loadUserClass(Person p)
    {
        List<Statement> userClasses = this.storeManager.getStore().tuplePattern(p.getUri(), RDF.TYPE, null);

        for(Statement s : userClasses)
        {
            UserClass userClass = Qbook.getInstance().getUserClass(s.getObject().stringValue());

            if(userClass != null)
                p.setUserClass(userClass);
        }
    }

    public boolean exists(String mail) {

        Literal mailLit = this.storeManager.getStore().getLiteral(mail);
        List<Statement> check = this.storeManager.getStore().tuplePattern(null, Foaf.getInstance().getMboxURI(), mailLit);

        if(check == null || check.isEmpty())
            return false;

        return true;
    }

    public Person authenticate(String mbox, String password) {

        List<Map> resultList = this.storeManager.getStore()
                        .runSPARQL("SELECT ?person ?firstName ?lastName ?createdAt ?extLong ?extString"
                + " WHERE { ?person <" + RDF.TYPE.toString() + "> <" + Foaf.getInstance().getPersonURI() +  "> ."
                + " ?person <" + Foaf.getInstance().getFirstNameURI() + "> ?firstName ."
                + " ?person <" + Foaf.getInstance().getLastNameURI() + "> ?lastName ."
                + " ?person <" + Qbook.getInstance().getCreatedAtURI() + "> ?createdAt ."
                + "  OPTIONAL { ?person <" + Qbook.getInstance().getExtLongIdURI() + "> ?extLong } ."
                + "  OPTIONAL { ?person <" + Qbook.getInstance().getExtStringIdURI() + "> ?extString } ."
                + " ?person <" + Foaf.getInstance().getMboxURI() + "> \"" + mbox + "\" ."
                + " ?person <" + Qbook.getInstance().getMd5PasswordURI() + "> \"" + password + "\" ."
                + "}");

        if(resultList == null || resultList.isEmpty())
        {
            System.out.println("keinen gefunden");
            return null;
        }

        Map m = resultList.get(0);

        Person p = new Person(((Value) m.get("person")).stringValue());
        p.setFirstName(((Value) m.get("firstName")).stringValue());
        p.setLastName(((Value) m.get("lastName")).stringValue());
        p.setCreatedAt(((Value) m.get("createdAt")).stringValue());

        if(m.get("extLong") != null)
             p.setExtLongId(((Value) m.get("extLong")).stringValue());
        if(m.get("extString") != null)
            p.setExtStringId(((Value) m.get("extString")).stringValue());
        
        p.setMbox(mbox);
        p.setMd5Password(password);
        this.loadUserClass(p);

        return p;
    }

    public List<Person> getAll() {

        List<Person> personList = new ArrayList<Person>();

        List<Map> resultList = this.storeManager.getStore()
            .runSPARQL("SELECT DISTINCT ?person ?firstName ?lastName ?mbox ?createdAt"
            + " WHERE { ?person <" + RDF.TYPE.toString() + "> "
                     + "<" + Foaf.getInstance().getPersonURI() +  "> ."
            + " ?person <" + Foaf.getInstance().getFirstNameURI() + "> ?firstName ."
            + " ?person <" + Foaf.getInstance().getLastNameURI() + "> ?lastName ."
            + " ?person <" + Foaf.getInstance().getMboxURI() + "> ?mbox ."
            + " ?person <" + Qbook.getInstance().getCreatedAtURI() + "> ?createdAt "
            + "}");

        for(Map m : resultList)
        {
            Person p = new Person(((Value) m.get("person")).stringValue());
            p.setFirstName(((Value) m.get("firstName")).stringValue());
            p.setLastName(((Value) m.get("lastName")).stringValue());
            p.setMbox(((Value) m.get("mbox")).stringValue());
            p.setCreatedAt(((Value) m.get("createdAt")).stringValue());

            this.loadUserClass(p);

            if(!personList.contains(p))
                personList.add(p);

        }
        return personList;
    }

    public void delete(Person p) {

        this.storeManager.getStore().removeTuplePattern(p.getUri(), null, null);

    }

    public void update(Person p) {

       this.storeManager.getStore().removeTuplePattern(p.getUri(), Foaf.getInstance().getFirstNameURI(), null);
       this.storeManager.getStore().add(p.getUri(), Foaf.getInstance().getFirstNameURI(), p.getFirstNameLiteral());

       this.storeManager.getStore().removeTuplePattern(p.getUri(), Foaf.getInstance().getLastNameURI(), null);
       this.storeManager.getStore().add(p.getUri(), Foaf.getInstance().getLastNameURI(), p.getLastNameLiteral());

       this.storeManager.getStore().removeTuplePattern(p.getUri(), Foaf.getInstance().getMboxURI(), null);
       this.storeManager.getStore().add(p.getUri(), Foaf.getInstance().getMboxURI(), p.getMboxLiteral());

       this.storeManager.getStore().removeTuplePattern(p.getUri(), Qbook.getInstance().getMd5PasswordURI(), null);
       this.storeManager.getStore().add(p.getUri(), Qbook.getInstance().getMd5PasswordURI(), p.getMd5PasswordLiteral());

    }

    public Person getIdentifiedPerson() throws IndividualAlreadyExistsException{

        int i = 0;
        String personBase = "/qbPerson";
        Date d = new Date();
        String timestamp = Long.toString(d.getTime());
        String id = QuestionbookConfiguration.getInstance().getBaseURL() + personBase + timestamp;
        URI uri = StoreManager.getInstance().getStore().getURIref(id);

        List check = this.storeManager.getStore().tuplePattern(uri, null, null);

        while(check != null || !check.isEmpty())
        {
            if(i == 10)
            {
                break;
            }

            d = new Date();
            timestamp = Long.toString(d.getTime());
            id = QuestionbookConfiguration.getInstance().getBaseURL() + personBase + timestamp;
            uri = this.storeManager.getStore().getURIref(id);

            check = this.storeManager.getStore().tuplePattern(uri, null, null);

            i++;
        }

        if(check == null || check.isEmpty())
        {
            Person p = new Person(uri);
            p.setCreatedAt(new Date());
            return p;
        }

        throw new IndividualAlreadyExistsException("Tried to create Person with URI " + id + " - but it already exists in the Store");
    }

    public void persistAnswerClass(Answer a) {

        this.storeManager.getStore().add(a.getAnswerer().getUri(), Qbook.getInstance().getHasAnswerPredicate(a.getAnswerClass()), a.getUri());

    }

    public void removeAnswerClass(Answer a) {

        this.storeManager.getStore().removeTuplePattern(a.getAnswerer().getUri(), Qbook.getInstance().getHasBadAnswerURI(), a.getUri());
        this.storeManager.getStore().removeTuplePattern(a.getAnswerer().getUri(), Qbook.getInstance().getHasDoubtAnswerURI(), a.getUri());
        this.storeManager.getStore().removeTuplePattern(a.getAnswerer().getUri(), Qbook.getInstance().getHasTrustAnswerURI(), a.getUri());
        this.storeManager.getStore().removeTuplePattern(a.getAnswerer().getUri(), Qbook.getInstance().getHasSuperAnswerURI(), a.getUri());
    }

    public void updateClassification(Person p) {

        this.storeManager.getStore().add(p.getUri(), RDF.TYPE, Qbook.getInstance().getUserClassURI(p.getUserClass()));
    }

    public void removeUserClass(Person p)
    {
        this.storeManager.getStore().removeTuplePattern(p.getUri(), RDF.TYPE, null);
    }

    public void persistCharma(Answer a, URI charma) {

        this.storeManager.getStore().add(a.getAnswerer().getUri(), charma, a.getUri());
    }

    public void removeCharma(Answer a) {

        this.storeManager.getStore().removeTuplePattern(a.getAnswerer().getUri(), Qbook.getInstance().getHasBadCharmaURI(), a.getUri());
        this.storeManager.getStore().removeTuplePattern(a.getAnswerer().getUri(), Qbook.getInstance().getHasMediateCharmaURI(), a.getUri());
        this.storeManager.getStore().removeTuplePattern(a.getAnswerer().getUri(), Qbook.getInstance().getHasGoodCharmaURI(), a.getUri());
    }

    public int getCardinality(Person p, URI charma)
    {
        List<Statement> resultList = this.storeManager.getStore().tuplePattern(p.getUri(), charma, null);

        if(resultList != null && resultList.size() > 0)
            return resultList.size();

        return 0;
    }

    public List<Person> suggestUsers(Category c) {

        List<Person> personList = new ArrayList<Person>();
        List<Person> returnList = new ArrayList<Person>();

        List<Map> resultList = this.storeManager.getStore()
                                .runSPARQL("SELECT ?person ?firstName ?lastName ?mbox ?question ?answer ?createdAt"
                + " WHERE { ?person <" + RDF.TYPE.toString() + "> <" + Foaf.getInstance().getPersonURI() +  "> ."
                + " ?person <" + Foaf.getInstance().getFirstNameURI() + "> ?firstName ."
                + " ?person <" + Foaf.getInstance().getLastNameURI() + "> ?lastName ."
                + " ?person <" + Foaf.getInstance().getMboxURI() + "> ?mbox ."
                + " ?person <" + Qbook.getInstance().getSaysURI() + "> ?answer ."
                + " ?person <" + Qbook.getInstance().getCreatedAtURI() + "> ?createdAt ."
                + " ?question <" + Qbook.getInstance().getAnswersURI() + "> ?answer ."
                + " ?question <" + Qbook.getInstance().getHasCategoryURI() + "> <" + c.getUri() + "> ."
                + " { ?person <" + RDF.TYPE + "> <" + Qbook.getInstance().getExpertUserURI() + "> } UNION "
                + " { ?person <" + RDF.TYPE + "> <" + Qbook.getInstance().getTrustUserURI() + "> } "
                + "}");
        
        for(Map m : resultList)
        {
            Person p = new Person(((Value) m.get("person")).stringValue());
          
            if(!personList.contains(p))
            {
                p.setFirstName(((Value) m.get("firstName")).stringValue());
                p.setLastName(((Value) m.get("lastName")).stringValue());
                p.setMbox(((Value) m.get("mbox")).stringValue());
                p.setCreatedAt(((Value) m.get("createdAt")).stringValue());
                
                this.loadUserClass(p);
                
                personList.add(p);
            }
        }

        for(Person p : personList)
        {
            if(returnList.size() == 3)
                break;
        
            if(p.getUserClass() == UserClass.ExpertUser)
                returnList.add(p);
        }
        for(Person p : personList)
        {
            if(returnList.size() == 3)
                break;

            returnList.add(p);
        }

        return returnList;
    }

    public Person getByMail(String mail) {

        Literal mailLit = this.storeManager.getStore().getLiteral(mail);
        List<Statement> check = this.storeManager.getStore().tuplePattern(null, Foaf.getInstance().getMboxURI(), mailLit);

        if(check != null && !check.isEmpty())
        {
            URI uri = this.storeManager.getStore().getURIref(check.get(0).getSubject().stringValue());
            Person p = this.get(uri);

            return p;
        }

        return null;
    }

    public Person getByExtLongId(Long id) {
        
        Literal idLit = this.storeManager.getStore().getLiteral(id);
        List<Statement> check = this.storeManager.getStore().tuplePattern(null, Qbook.getInstance().getExtLongIdURI(), idLit);

        if(check != null && !check.isEmpty())
        {
            URI uri = this.storeManager.getStore().getURIref(check.get(0).getSubject().stringValue());
            Person p = this.get(uri);

            return p;
        }

        return null;
    }

    public Person getByExtStringId(String id) {
        
        Literal idLit = this.storeManager.getStore().getLiteral(id);
        List<Statement> check = this.storeManager.getStore().tuplePattern(null, Qbook.getInstance().getExtStringIdURI(), idLit);

        if(check != null && !check.isEmpty())
        {
            URI uri = this.storeManager.getStore().getURIref(check.get(0).getSubject().stringValue());
            Person p = this.get(uri);

            return p;
        }

        return null;
    }


   
}
